package com.jiangruan.javaproject.address;

import com.jiangruan.javaproject.utils.Base58;
import com.jiangruan.javaproject.utils.Hash;
import com.sun.org.apache.xalan.internal.res.XSLTErrorResources_zh_CN;

import java.security.*;
import java.security.interfaces.ECPublicKey;
import java.security.spec.ECGenParameterSpec;
import java.security.spec.ECPoint;
import java.util.Arrays;
import java.util.Scanner;

/**
 * @author:liuzuoqiang
 * @email:2639571477@qq.com
 * @desc:  该类用于推导生成原始格式的比特币地址，并进行规则校验
 * @datetime: 2021/12/30 2:32
 */

public class BitCoinAddress {
    /**
     * 该方法用于生成一个随机的EC算法的密钥对，并返回该密钥对
     *
     * @return 密钥对对象
     */
    private KeyPair generateKey() {
        try {
            KeyPairGenerator generator = KeyPairGenerator.getInstance("EC");
            //parameter:参数
            //json-rpc: params
            //spec: specified  明确的，指定的 special 特别的
            ECGenParameterSpec spec = new ECGenParameterSpec("secp256k1");
            generator.initialize(spec);//initialize 初始化  init,initial
            return generator.genKeyPair();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (InvalidAlgorithmParameterException e) {
            e.printStackTrace();
        }
        return null;

    }

    /**
     * 该方法用于推导生成原始格式的比特币地址，并返回地址字符串
     *
     * @param keyPair 随机的密钥对信息
     * @return 生成的比特币地址
     */
    private String generateAddress(KeyPair keyPair) {
        //publicKey: 公钥密码学的公钥
        ECPublicKey PublicKey = (ECPublicKey) keyPair.getPublic();
        //原始格式的公钥：04+x坐标(32字节) + y 坐标(32字节)
        byte[] pubKey = toUnCompressPub(PublicKey);

        //哈希
        byte[] sha256 = Hash.SHA256(pubKey);
        byte[] pubhash = Hash.RIPEMD160(sha256);

        //在公钥哈希前面添加版本号
        byte[] version = new byte[1];
        version[0] = 0x00;

        byte[] ver_pubhsh = new byte[version.length + pubhash.length];
        System.arraycopy(version, 0, ver_pubhsh, 0, version.length);
        System.arraycopy(pubhash, 0, ver_pubhsh, version.length, pubhash.length);

        //对版本号_公钥哈希的序列进行双哈希，取前四个字节
        byte[] hash1 = Hash.SHA256(ver_pubhsh);
        byte[] hash2 = Hash.SHA256(hash1);

        byte[] check = new byte[4];
        System.arraycopy(hash2, 0, check, 0, check.length);

        //将check拼接到ver_pubhash后面，组成新的序列
        byte[] ver_pubhash_check = new byte[ver_pubhsh.length + check.length];
        System.arraycopy(ver_pubhsh, 0, ver_pubhash_check, 0, ver_pubhsh.length);
        System.arraycopy(check, 0, ver_pubhash_check, ver_pubhsh.length, check.length);


        //base58编码
        return Base58.encode(ver_pubhash_check);


    }


    /**
     * 该方法用于拼接得到非压缩格式 (原始格式) 的公钥， 并返回原始公钥
     *
     * @param publicKey ec公钥
     * @return 原始格式公钥
     */
    public byte[] toUnCompressPub(ECPublicKey publicKey) {
        ECPoint point = publicKey.getW();
        byte[] xBytes = point.getAffineX().toByteArray();
        byte[] yBytes = point.getAffineY().toByteArray();

        //数组拷贝
        //原始格式结构： 04(1) + x坐标(32) + y坐标(32)
        byte[] pubKey = new byte[1 + 2 * 32];
        pubKey[0] = 04;

        //判断x坐标
        if (xBytes.length == 32) {
            System.arraycopy(xBytes, 0, pubKey, 1, xBytes.length);
        } else if (xBytes.length > 32) {
            System.arraycopy(xBytes, 1, pubKey, 1, 32);
        }

        //y坐标
        if (yBytes.length == 32) {
            System.arraycopy(yBytes, 0, pubKey, 1, yBytes.length);
        } else if (yBytes.length > 32) {
            System.arraycopy(yBytes, 1, pubKey, 33, 32);
        }

        return pubKey;
    }


    /**
     * 生成一个比特币地址，并返回
     *
     * @return 返回生成的比特币地址
     */
    public String newAddress() {
        KeyPair keyPair = generateKey();
        return generateAddress(keyPair);
    }

    /**
     * 该方法用于校验比特币地址字符串是否符合规范，并返回结果
     *
     * @param address 要校验的比特币地址
     * @return 是否符合规范
     */
    public boolean validateAddress(String address) {
        if ("".equals(address) || address == null) {
            return false;

        }
        byte[] ver_pubhash_check = Base58.decode(address);
        if (ver_pubhash_check.length < 4) {
            return false;
        }


        //截取后四个字节，作为待验证的校验位
        byte[] check = new byte[4];
        System.arraycopy(ver_pubhash_check,
                ver_pubhash_check.length - 4,
                check,
                0,
                check.length);

        byte[] ver_pobhash = new byte[ver_pubhash_check.length - 4];
        System.arraycopy(ver_pubhash_check,
                0,
                ver_pobhash,
                0,
                ver_pobhash.length);


        //双hash
        byte[] hash1 = Hash.SHA256(ver_pobhash);
        byte[] hash2 = Hash.SHA256(hash1);

        byte[] check_sum = new byte[4];
        System.arraycopy(hash2, 0, check_sum, 0, check_sum.length);

        return Arrays.equals(check, check_sum);

    }

//    public static void main(String[] args) {
//        BitCoinAddress address = new BitCoinAddress();
//        String address1 = address.newAddress();
//        System.out.println(address1);
//        boolean b = address.validateAddress(address1);
//        System.out.println(b);
//    }
}


